Skip to content

fix(ui): the workbench audit — the graph survives scale, choudoufu edges and words, ⌘K by address, and the license (#393, #394) - #395

Closed
lex00 wants to merge 14 commits into
feat/workbench-386from
feat/audit-393
Closed

fix(ui): the workbench audit — the graph survives scale, choudoufu edges and words, ⌘K by address, and the license (#393, #394)#395
lex00 wants to merge 14 commits into
feat/workbench-386from
feat/audit-393

Conversation

@lex00

@lex00 lex00 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Closes #393, #394. Stacked on #392 (the workbench); every measurement below was taken on the workbench entries it added.

The audit's headline was that the graph did not survive scale: terralith-4, 301 choudoufu resources, rendered as a 144,010 × 316 strip with nothing clickable. It is now a 7,660 × 5,162 picture with 266 edges, module sub-boxes and a count badge, and ⌘K lands on any address.

What changed

Layout at scale (src/edgeless.ts, src/render.ts)

  • An edgeless box wraps its cards into rows through layout-only chains that never reach the returned IR or the SVG; a box with edges packs its connected components instead of letting dagre string them out, and a far-off-aspect component (a 40-way star) wraps into a grid. Boxes under 4:1 are untouched, so every existing picture is unchanged.
  • One sub-box per module.<name>[...] instance inside a choudoufu member, drawn from where the cards landed, ids stable.
  • Count badges on every member box (301 resources · 84 bound · 85 unowned · 132 neutral), and a collapse=1 lens that folds a box over 40 cards into one summary card; a palette command toggles it.

Edges (src/choudoufu-refs.ts) — a choudoufu member's read also runs the terraform lexicon over the same directory when it is resolvable, and joins its reference edges onto the roster by address, module instance by module instance. count/for_each pairs join key-to-key when both ends expand over the same keys, falling back to the full product otherwise, and every such edge is marked inferred. Without the lexicon the note names the install line instead of claiming nothing references anything.

Search — ⌘K matches node ids and addresses from the painted graph, Enter selects the card and pans the viewBox onto it. Works in a static export.

Words and cards — the legend, the statusbar and the Model tab speak choudoufu's words (bound / unowned / pending / not observed) on a choudoufu estate, derived from the member kinds and carried on the overlay meta; a mixed estate keeps chant's words and says so. A card is painted with its address alone and the rows the box does not already carry (owned by: stays; rung: only when it is not the common one); ids are untouched, the display id is restored on the finished SVG, and a label two cards would share is used for neither. The UNOWNED inspect leads with the two adopt tags as one copyable line; choudoufu 0.16's JSON carries no adoption command, so behold composes none.

Boot, zoom, notes, bugs — a non-chant estate boots on resources; runtime is offered only where a member could have runtime children; /api/resources no longer 500s on a non-chant estate; "estate of 1 project"; the roots note survives the logical zoom, has a short form for the strip and the full text on a tooltip and in the Model tab; waterpark's empty member box is gone (the root boxes take the member's name when the estate has more than one member); a data-URI favicon stops the 404 every load fetched.

License (#394) — Apache-2.0 to match chant: LICENSE, the package.json field, LICENSE + THIRD_PARTY.md + licenses/ in the shipped files (asserted by test), and behold export copies the notices into every bundle. Republishing is a release step.

Numbers

estate before after
terralith-4, detail 2, env live 144,010 × 316, 0 edges 7,660 × 5,162, 266 edges, 3 boxes
terralith-4, lexicon absent same strip 8,186 × 3,436, 0 edges, note names the install line
waterpark, detail 2 15,833 × 1,164 3,536 × 3,138
waterpark, detail 3 60,230 × 1,404 7,142 × 6,974
choudoufu-workbench, env live 9,564 × 520 2,890 × 2,468, whole estate on one screen

bash e2e/workbench-e2e.sh on the author's machine, on this branch: 8 entries green, 3 skipped (the two chant examples have no node_modules and an in-place entry is never installed; fountain-ops is excluded by name), 499s, no scratch container left. terralith-4: 301 nodes, 266 edges, 1.58:1 at detail 2, badged, collapses to one card; terralith-4-adopt: 85 unowned before the by-hand live-import, 301 bound after; the cohorts and the workbench now carry 1 to 51 edges each; waterpark untouched.

Built how

Five Opus agents in worktrees off this branch, merged by hand in two waves; the three merge commits carry the reconciliation (one word for the "directory is its own member" shape, bands read off real ids, footprints measured with the face overrides). just check and npm run smoke:ui green at every merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq

lex00 and others added 14 commits September 9, 2026 21:43
…s the notices it redistributes (#394)

@intentius/behold has been on npm since 0.17.0 with no grant anywhere: no
LICENSE, no package.json field, nothing in the tarball. Apache-2.0 to match
chant, which behold is a control plane on. LICENSE, THIRD_PARTY.md and
licenses/ join the files list and are asserted there by test, and
`behold export` copies them into the bundle, since whoever serves an export
redistributes the vendored themes, artwork and icons.

Republishing so the registry metadata stops being blank is a release, not
this commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
… for non-chant estates, runtime only where it applies, no 500 on /api/resources, the roots note everywhere it belongs (#393)

Seven of #393's findings, all of them things a person meets in the first screen
of a choudoufu or Terraform estate.

`/api/project` grows two facts, and the SPA reads both before its first fetch:

  - `memberKinds` — the served members' kinds, in composition order. When none
    of them is `chant` the SPA opens on `resources`. `components` is a chant
    projection of a chant project's own component DAG, so a choudoufu estate or
    a bare Terraform directory opened on "the components lens doesn't apply to
    a composed estate yet" — an apology, every time. #182's zero-node fallback
    still covers a chant project that declares no components, and picking
    components later still gets the honest note: it now leads the note instead
    of being displaced by the estate's own.
  - `runtimeCapable` — whether any member declares a lexicon whose live read has
    an owner-reference chain to descend (`RUNTIME_LEXICONS`: `k8s`). chant paints
    a node `runtime` only where it stamps `runtimeOwner`, and that is a
    Kubernetes mechanism, so the stop on every choudoufu estate could only ever
    answer "nothing below the declaration boundary". The palette and the View
    tab read one list, so a stop is offered on both surfaces or on neither.

`/api/resources` answers `{byComponent: {}}` for a primary that is not a chant
member instead of shelling chant at an estate chant cannot read — that 500
(`No lexicon detected`) was in the console on every choudoufu boot. The same
shape carve mode returns, and the one the SPA already reads as "no facet here".

The roots note (#384) is built by one function in `/api/graph` and returned by
all three of its branches. The logical lens returns before the bottom of the
route, so the one line that explained a Terraform picture vanished at exactly
the zoom that needed it most. Both notes come in two forms now: `note` as before
and `noteShort` (`5 roots · 2 skipped · 189 blocks not drawn`) for the 260px
strip, with the long one on the strip's tooltip and printed whole on the Model
tab. The server writes both — the SPA authors no notes, and truncating this one
on a sentence boundary would keep the list of root names and drop the counts.

`groupTerraformByRoot` drops a box every node has left rather than drawing it
empty: a served directory arrives composed, `composeStacks` boxes the whole
member, the root boxes then take those same nodes, and a node sits in exactly
one box — which is what put an empty box named `access` on water park's canvas.
Nesting was not on offer (`boxes: "byStack"` is flat; only the architecture lens
nests), so what the member box was there to say moves into the root box's title:
`<member>/<root>` when the estate holds more than one member, the bare root name
when it does not — two members with a root apiece named `prod` would otherwise
merge silently. And an estate of one member reads "estate of 1 project".

The page declares its own icon as a data URI too: "nothing in the console"
included the 404 every load had always fetched for itself.

Verified against the real estates, not only the stub: `behold serve
../waterpark/access` (5 roots, 58 cards, 219 at attributes) and the
`choudoufu-cohort-iam-ecr` workbench entry over a scratch floci, driven in
headless Chrome — the boot zoom, both zoom surfaces, the strip, the tooltip, the
Model tab, the box list and a clean console on each. `smoke/stub.mjs` grows a
third mode (`nonChant`) carrying water park's own note, so `npm run smoke:ui`
asserts what that driver asserted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…44010x316 to 8186x3436 (#393)

A box whose cards reference nothing lays out as one dagre rank, because a rank
is a row and edges are the only thing that makes a second one. On terralith-4 —
301 choudoufu cards, one member box, no edges — that was an SVG of 144010 x 316,
a 455:1 strip where "fit to view" is a one-pixel line and nothing is clickable.

pinhole has no lever for it: `ConceptLayoutOptions` is rankdir / ranksep /
nodesep / groups / fit, `rankdir` only turns the strip on its side, and
`layoutIr` reads ranks from `ir.edges` and nothing else. So src/edgeless.ts
chains each card to the card `cols` positions later — column chains, so card i
lands on rank floor(i / cols) — with `cols` derived from the cards' own painted
footprints and a target aspect. The chains go into a COPY of the IR that only
the layout call ever sees, so `renderSvg` paints from the caller's own IR, the
route returns the caller's own IR, and `edgelessNote` still reads a real zero;
they are flagged `viaAttr: "_layout-row"` all the same, and `withoutRowChains`
strips them.

Per box, not per graph: a root that wires its own cards keeps the layout dagre
gave it, and one that does not wraps even when the graph has edges elsewhere.
Below six cards nothing changes, so every small picture in the suite is
byte-identical.

One thing had to be settled to make it correct: a node can be listed by two
boxes at once (a served Terraform directory is a member box AND its roots are
boxes, so waterpark's cards appear twice). pinhole parents a node to the LAST
group that claims it, so this reads ownership the same way — and `packMemberBoxes`
now does too, where the outer box's packing block used to be the union of every
card in the picture, leaving the shelf packer one block and nothing to do.

Measured on the workbench, `/api/graph`'s svg viewBox:

  terralith-4  detail 2 and 3, source and env=live
    144010 x 316   455.7:1   ->   8186 x 3436   2.4:1
  waterpark    detail 2
     15833 x 1164   13.6:1   ->   7499 x 1798   4.2:1
  waterpark    detail 3
     60230 x 1404   42.9:1   ->  29958 x 1982  15.1:1

waterpark at detail 3 is the case this does not answer, and the number says so:
its `prod` root is 104 cards in 37 connected components, which dagre puts side
by side on three ranks. That is a wide rank rather than a single one, and
packing a box's components the way `packComponents` packs the whole canvas is a
post-layout move with a box resize behind it, not an edge.

The workbench e2e asserts the ratio on the real terralith, so the strip cannot
come back unnoticed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…393)

The terralith calls one module twice — module.team_pod["pod-a"] and ["pod-b"],
24 cards each — and both sets sat loose among the other 253, indistinguishable
from the root module's own resources. The instance is the grouping a person
reads that estate by, and the ["pod-a"] is the whole point: two calls of one
module are two boxes.

Deliberately not a grouping by type. An estate of 248 identity resources would
draw as one box called aws_iam_role and say nothing a card does not already say.

pinhole's `layoutIr` parents a node to ONE cluster (dist/concept.d.ts), so a box
inside a box is not something the layout can be asked for. It does not have to
be: the row chains from the previous commit already give each band its own whole
rows, so a band's cards are a contiguous rectangle of the grid and the sub-box is
that rectangle — drawn at depth 1, clamped inside the member box so an inflated
rect can never poke out of the box it claims to be inside.

Ids stay exactly as they arrived. The sub-box is `<member>/<module instance>`,
which is what the SPA addresses a box by (`data-group-id`) and stores its
hand-set size under; the TITLE drops the member, which the enclosing box already
says. No card id moves, so the overlay, the diff pane and the moves still join.

Verified on terralith-4 (301 cards, env=live): three boxes, `terralith-4` and
the two module instances, 8186 x 3436 unchanged from the wrap alone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…or the big ones (#393)

A box holding 301 cards should be able to say it holds 301 cards, and it should
be possible to read an estate at the level of its members without drawing every
card in them.

The badge rides the `GroupBox.badge` seam #357 opened for the operator's own box
(pinhole#122), keyed by box the way `groupMarks` already is, and it is on for
every estate render, collapsed or not. The counts speak the ESTATE's vocabulary,
not the overlay legend's: choudoufu's words are bound / unowned / pending /
neutral, and calling 84 derived-identity cards "managed" on an unadopted estate
tells a reader a third of it is already theirs. Measured on the workbench:

  terralith-4        source   301 resources
  terralith-4        live     301 resources · all bound
  terralith-4-adopt  live     301 resources · 84 bound · 85 unowned · 132 neutral

`?collapse=1` (⌘K → "Collapse large boxes" / "Expand all") shuts every box over
40 cards: the cards leave the graph and one summary card takes their place,
carrying that same sentence and the colour its cards agreed on, id namespaced
`box:<member>`. An edge that touched a collapsed card re-points at the summary,
so a cross-member reference still draws from the member as a whole; one that
ends up with both feet inside the same summary is dropped rather than looped.
The route returns the COLLAPSED ir, so the inspect pane, the edge count and
every note speak about the picture on screen. Below the limit nothing changes at
all, which is why the small estates in the suite render byte-identical with the
flag on.

`collapse` joins LENS_PARAMS in web/app.js AND src/export.ts, which must agree —
`canonicalKey` is what a static bundle's snapshots are filed under.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…ps its short form beside the collapse note

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…inspect leads with the line to run (#393)

Two of the audit's wording findings, on the estate the audit measured.

The legend, the statusbar counts and the Model tab's drift block said
managed / foreign / pending / unobserved on every estate, which are chant's
words. On the unadopted terralith that reads 84 cards as "managed" before a
single adoption has happened - they are bound by DERIVED IDENTITY, which
carries no marker at all - so a third of the estate looks like it is already
yours. The server now derives a `vocabulary` from the served members' kinds
(src/status-vocabulary.ts) and puts it on the overlay meta beside the counts;
the SPA renders the legend, the counts and the inspect status row from it and
falls back to chant's words when there is none. On a choudoufu estate that
reads bound / unowned / pending / not observed. The colours do not move.

`not observed` is the one word here that is not choudoufu's own: choudoufu has
no single name for that bucket - it is live-ls's `gaps[]`, the live-plan
omission reasons that mean "could not answer", and the addresses neither
document mentions. `omitted` is the near miss and is a miss, because `ABSENT`
is an omission too and behold paints that one pending.

A mixed estate keeps chant's words - one colour cannot carry two meanings -
and the legend's heading says why in its tooltip.

The bound-by-derived row now says the consequence out loud: "no marker on the
object yet".

And the UNOWNED inspect. What a person wants off that card is the write to
run; it used to be a paragraph in monospace with the two adopt tags under it
as JSON. `adopt` is now one line - `tofu-estate=... tofu-address=...`, the
values choudoufu itself named - first in the declared section, with the copy
button the moves panel already gives a `live-mv` line, and the paragraph below
it. behold composes no command: verified against a real `choudoufu live-plan
-json` on v0.16.0 that no `hint` or `adopt_command` reaches an `unowned[]`
entry (`StatelessUnowned` marshals the two marker values and nothing else), and
the tagging API differs per resource type, so guessing one would be worse than
saying nothing. If a release ever sends one, `adoptLine` prefers it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
A choudoufu card inside the `terralith-4` box was five lines, three of which
the box already said:

    terralith-4/aws_ecs_cluster.main
    aws_ecs_cluster · choudoufu
    rung: tag-governable
    estate: terralith-4

On 301 cards. The member name is the box's title, the estate is the box's
estate, `tag-governable` is what all but a handful of instances are, and
`choudoufu` is the member kind - one fact about the whole box, none about this
card. A Terraform card had the same shape (`access/baseline/
aws_iam_policy.boundary` inside the `baseline` box).

Now: the address, the type, and whatever is unusual - a rare rung, an estate
the box does not stand for, and, new here, `owned by: <estate>` on a card whose
live object belongs to a neighbour, which is the case the estate row was meant
to carry and never did.

The id does not move. `<member>/<address>` is what the overlay joins on, what
/api/diff keys, what the move plans name and what a click looks up. pinhole
0.3.8 titles a card from `node.id` and has no per-node title channel, so
src/card-face.ts feeds the paint a display IR and puts the real ids back on the
finished SVG. That sets the one limit: a label two cards would share is used
for neither, because a `data-node-id` two cards claim restores to one of them.
The live-mv workbench is one estate split four ways and every one of its 42
addresses is declared twice, so it keeps its full ids and reads as it did; a
member whose addresses are its own shortens every card.

Measured on a real unadopted terralith (79 cards, stock terraform applied,
choudoufu 0.16.0): every title short, one field row left on 41 of 79.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…eal ids, footprints measured with the face overrides

cardFaces paints a choudoufu card from a display IR with the address as its
id and the lexicon blanked, so moduleBandOf over that IR found no module
band and the sub-boxes vanished; the band is read off the real ids through
the one-to-one node map. The footprints the row grid and the band boxes use
now take the same field overrides the layout and the paint get, so a card
with fewer rows does not inflate the box around it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…d to the roster by address (#393)

`choudoufu live-check -json` states the roster and `references[]`, and that
second list is cross-estate by construction — data sources filtered on another
estate's marker tags. So `terralith-4` (301 roles, policies, attachments and
profiles) drew no edge at all and the graph asserted "nothing in this estate
references anything else", which is false about every one of them.

Nothing was reading the references. chant's terraform lexicon reads the same
HCL and, at 0.61 (chant#2265), resolves a block's `"${…}"` interpolations. A
choudoufu member's read now also runs the terraform kind's reader over its own
directory — one root, the estate itself, through the scratch-project machinery
src/terraform-member.ts already has, so nothing is written under the estate —
and joins the two documents by address (src/choudoufu-refs.ts). behold still
parses no HCL: the join is two lists of strings.

The lexicon names BLOCKS in a path of module CALLS; choudoufu names INSTANCES
in a path of module INSTANCES. One lexicon edge is therefore a product, and
four rules cut it down, in decreasing order of what Terraform itself
guarantees: kept inside one module instance (scoping, not a guess); joined key
to key when both ends expand over the same keys (266 edges on terralith-4,
against 666 of which ~400 would say a role is attached to a policy it is not);
landing on a module call's whole interior; dropped whole when either end is a
`var`, a `locals` or anything else the roster declares no card for (17 of the
estate's 175 lexicon edges). Every edge is `inferred` and carries the lexicon's
own attribute name, so the card says `role` or `policy_arn` rather than "ref".

Without the lexicon there are no edges and the note says so, carrying the
terraform kind's own install line rather than a claim behold has no reader to
make; the lexicon's version joins the member's cache stamp, so installing it
invalidates the edgeless IR instead of serving it forever.

The layout half is `packBoxComponents` (src/render.ts). src/edgeless.ts wraps a
box whose cards reference NOTHING, so a box that has edges is never wrapped and
terralith-4 came back a 110996 x 628 strip the day the join landed. The pass
packs a box's components into shelves and resizes the box around them, re-laying
each component on its own first (inside a cluster dagre interleaves them — a
six-card cluster's bounding box spanned 46598 units) and wrapping a component
that is a strip either way (the DNS fan is 19564 x 400 upright, 900 x 10046 on
its side). A box under 4:1 is left exactly as dagre laid it, so every existing
picture is unchanged. Measured on the workbench: terralith-4 7660 x 5162 with
266 edges and both module sub-boxes still drawn, 8186 x 3436 unchanged with the
lexicon absent; waterpark 3536 x 3138 at detail 2 and 7142 x 6974 at detail 3,
from 4.2:1 and 15.1:1 — the case #393's wrap did not answer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
… a pan that lands on the card (#393)

The palette had zoom, env, panel and deploy commands and nothing that took a
node, which on a 301-card estate meant there was no way to reach a card except
to find it by eye.

Two characters in, ⌘K now matches the ids and addresses of the graph the page
is currently showing — the SPA already holds the IR it painted — and offers up
to twelve `node: <address>` rows with the member and the kind on a second line.
That second line is not decoration: two members of one estate can declare the
same address, and the address alone cannot tell them apart. Prefix matches rank
first, so `aws_iam_role.team_00` reaches `team_0007_role` before a card that
merely mentions it, and the rows are built per keystroke rather than on open —
301 rows in front of somebody who typed nothing would bury every command.

Enter takes the same path a click on the card does (`selectNode`: the inspect
pane and the highlight) and then pans the graph onto it. There was no
programmatic pan before this, only "⤢ fit"'s reset, so `revealNode` is the
smallest one: the same viewBox the wheel and the drag drive, centred on the
card's own rectangle read off the DOM, at a window nine card-widths across and
never wider than the fit. A node the current zoom elided says so in a toast
rather than panning to nowhere.

Nothing here fetches — the IR is the one the page painted and the pan is a
viewBox — so a static export answers ⌘K exactly as a served project does.

Driven in headless Chrome against the stub (smoke/stub.mjs's `nonChant` estate
now paints composed ids on an 8000 x 3000 canvas with one card in the far
corner, and two members declaring one address) and by hand against the
terralith-4 workbench entry: typing `aws_iam_role.team_0007_role` offers one
row reading `t4 · aws_iam_role · choudoufu`, and Enter moves the viewBox from
`0 0 7660 5162` to `289 744 2808 1892` with the card selected and inside it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
… layout pass measures the painted card

The box packer and the band boxes now read the module band off the real ids
(the display IR blanks a choudoufu card's lexicon), and every footprint pass
takes the face overrides through one per-IR map, so no box is sized for rows
the paint dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…he overlay vocabulary test needs no binary

CI has no choudoufu; the overlay of a choudoufu estate read through
captureChoudoufu directly and refused there, so the vocabulary assertion
met an error body. createApp now hands a test's runner to the via as well,
and the route test's fake answers live-plan from the recorded documents.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
@lex00
lex00 deleted the branch feat/workbench-386 September 10, 2026 21:36
@lex00 lex00 closed this Sep 10, 2026
lex00 added a commit that referenced this pull request Sep 10, 2026
…ges and words, ⌘K by address, and the license (#393, #394)

The audit's fixes as one commit on main. Layout at scale (an edgeless box wraps its cards; a box with edges packs its components; module sub-boxes; count badges and a collapse lens), intra-estate edges for a choudoufu member from the terraform lexicon, ⌘K by address, choudoufu's own words in the legend, cards that say what their box does not, the UNOWNED inspect leading with the adopt tags, boot on resources, runtime only where it applies, no 500 on /api/resources, the roots note everywhere it belongs, and Apache-2.0 declared and shipped with the notices carried by export. PR #395 has the milestone-by-milestone account.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
@lex00

lex00 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Closed by GitHub when its base branch went with #392's merge; continued as #407 on the same branch, rebuilt as one commit on main.

lex00 added a commit that referenced this pull request Sep 10, 2026
…ges and words, ⌘K by address, and the license (#393, #394) (#407)

The audit's fixes as one commit on main. Layout at scale (an edgeless box wraps its cards; a box with edges packs its components; module sub-boxes; count badges and a collapse lens), intra-estate edges for a choudoufu member from the terraform lexicon, ⌘K by address, choudoufu's own words in the legend, cards that say what their box does not, the UNOWNED inspect leading with the adopt tags, boot on resources, runtime only where it applies, no 500 on /api/resources, the roots note everywhere it belongs, and Apache-2.0 declared and shipped with the notices carried by export. PR #395 has the milestone-by-milestone account.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant